﻿/*
-----------------------------------------------------------------------------
This source file is part of Cell Cloud.

Copyright (c) 2009-2012 Cell Cloud Team - cellcloudproject@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#ifndef CellSharedPtr_h
#define CellSharedPtr_h

#include "CellPrerequisites.h"

namespace cell
{
/** \addtogroup Core
 *  @{
 */
/** \addtogroup General
 *  @{
 */

/// 智能指针在析构时释放内存的方法。
enum SharedPtrFreeMethod
{
	/// 使用 CELL_DELETE 释放内存
	SPFM_DELETE,
	/// 使用 CELL_DELETE_T 释放内存（仅支持 MEMCATEGORY_GENERAL）
	SPFM_DELETE_T,
	/// 使用 CELL_FREE 释放内存（仅支持 MEMCATEGORY_GENERAL）
	SPFM_FREE
};

/** 使用引用计数的共享指针。
@remarks
	该智能指针使用引用计数方式工作，当引用计数为 0 时删除对象。
@par
	该类是线程安全的。
*/
template<class T> class SharedPtr
{
protected:
	T* pRep;
	unsigned int* pUseCount;
	SharedPtrFreeMethod useFreeMethod;	// 内存删除方式
public:
	CELL_AUTO_SHARED_MUTEX // public to allow external locking

	/** 默认构造函数。不能用于初始化 SharedPtr
	@remarks
		直接使用该构造函数构建 SharedPtr 可能会带来引用计数错误。
	*/
	SharedPtr() : pRep(0), pUseCount(0), useFreeMethod(SPFM_DELETE)
    {
        CELL_SET_AUTO_SHARED_MUTEX_NULL
    }

	/** 构造函数。
	@param rep 指针的所属。
	@param freeMethod 内存释放方法。
	*/
    template< class Y >
	explicit SharedPtr(Y* rep, SharedPtrFreeMethod freeMethod = SPFM_DELETE) 
		: pRep(rep)
		, pUseCount(rep ? CELL_NEW_T(unsigned int, MEMCATEGORY_GENERAL)(1) : 0)
		, useFreeMethod(freeMethod)
	{
        CELL_SET_AUTO_SHARED_MUTEX_NULL
		if (rep)
		{
			CELL_NEW_AUTO_SHARED_MUTEX
		}
	}

	SharedPtr(const SharedPtr& r)
        : pRep(0), pUseCount(0), useFreeMethod(SPFM_DELETE)
	{
		// lock & copy other mutex pointer
            
        CELL_SET_AUTO_SHARED_MUTEX_NULL
        CELL_MUTEX_CONDITIONAL(r.CELL_AUTO_MUTEX_NAME)
        {
			CELL_LOCK_MUTEX(*r.CELL_AUTO_MUTEX_NAME)
			CELL_COPY_AUTO_SHARED_MUTEX(r.CELL_AUTO_MUTEX_NAME)
			pRep = r.pRep;
			pUseCount = r.pUseCount; 
			useFreeMethod = r.useFreeMethod;
			// Handle zero pointer gracefully to manage STL containers
			if(pUseCount)
			{
				++(*pUseCount); 
			}
        }
	}

	SharedPtr& operator=(const SharedPtr& r)
	{
		if (pRep == r.pRep)
			return *this;
		// Swap current data into a local copy
		// this ensures we deal with rhs and this being dependent
		SharedPtr<T> tmp(r);
		swap(tmp);
		return *this;
	}
		
	template< class Y>
	SharedPtr(const SharedPtr<Y>& r)
        : pRep(0), pUseCount(0), useFreeMethod(SPFM_DELETE)
	{
		// lock & copy other mutex pointer

        CELL_SET_AUTO_SHARED_MUTEX_NULL
        CELL_MUTEX_CONDITIONAL(r.CELL_AUTO_MUTEX_NAME)
        {
			CELL_LOCK_MUTEX(*r.CELL_AUTO_MUTEX_NAME)
			CELL_COPY_AUTO_SHARED_MUTEX(r.CELL_AUTO_MUTEX_NAME)
			pRep = r.getPointer();
			pUseCount = r.useCountPointer();
			useFreeMethod = r.freeMethod();
			// Handle zero pointer gracefully to manage STL containers
			if(pUseCount)
			{
				++(*pUseCount);
			}
        }
	}
	template< class Y >
	SharedPtr& operator=(const SharedPtr<Y>& r)
	{
		if (pRep == r.getPointer())
			return *this;
		// Swap current data into a local copy
		// this ensures we deal with rhs and this being dependent
		SharedPtr<T> tmp(r);
		swap(tmp);
		return *this;
	}

	virtual ~SharedPtr()
	{
        release();
	}


	inline T& operator*() const { assert(pRep); return *pRep; }
	inline T* operator->() const { assert(pRep); return pRep; }
	inline T* get() const { return pRep; }

	/** 绑定引用到 SharedPtr
	*/
	void bind(T* rep, SharedPtrFreeMethod freeMethod = SPFM_DELETE)
	{
		assert(!pRep && !pUseCount);
        CELL_NEW_AUTO_SHARED_MUTEX
		CELL_LOCK_AUTO_SHARED_MUTEX
		pUseCount = CELL_NEW_T(unsigned int, MEMCATEGORY_GENERAL)(1);
		pRep = rep;
		useFreeMethod = freeMethod;
	}

	inline bool unique() const { CELL_LOCK_AUTO_SHARED_MUTEX assert(pUseCount); return *pUseCount == 1; }
	inline unsigned int useCount() const { CELL_LOCK_AUTO_SHARED_MUTEX assert(pUseCount); return *pUseCount; }
	inline unsigned int* useCountPointer() const { return pUseCount; }

	inline T* getPointer() const { return pRep; }
	inline SharedPtrFreeMethod freeMethod() const { return useFreeMethod; }

	inline bool isNull(void) const { return pRep == 0; }

    inline void setNull(void)
	{ 
		if (pRep)
		{
			// can't scope lock mutex before release in case deleted
			release();
			pRep = 0;
			pUseCount = 0;
		}
    }

protected:

    inline void release(void)
    {
		bool destroyThis = false;

        /* If the mutex is not initialized to a non-zero value, then
            neither is pUseCount nor pRep.
            */

        CELL_MUTEX_CONDITIONAL(CELL_AUTO_MUTEX_NAME)
		{
			// lock own mutex in limited scope (must unlock before destroy)
			CELL_LOCK_AUTO_SHARED_MUTEX
			if (pUseCount)
			{
				if (--(*pUseCount) == 0) 
				{
					destroyThis = true;
	            }
			}
        }
		if (destroyThis)
			destroy();

        CELL_SET_AUTO_SHARED_MUTEX_NULL
    }

    virtual void destroy(void)
    {
        // IF YOU GET A CRASH HERE, YOU FORGOT TO FREE UP POINTERS
        // BEFORE SHUTTING CELL DOWN
        // Use setNull() before shutdown or make sure your pointer goes
        // out of scope before CELL shuts down to avoid this.
		switch(useFreeMethod)
		{
		case SPFM_DELETE:
			CELL_DELETE pRep;
			break;
		case SPFM_DELETE_T:
			CELL_DELETE_T(pRep, T, MEMCATEGORY_GENERAL);
			break;
		case SPFM_FREE:
			CELL_FREE(pRep, MEMCATEGORY_GENERAL);
			break;
		};
		// use CELL_FREE instead of CELL_DELETE_T since 'unsigned int' isn't a destructor
		// we only used CELL_NEW_T to be able to use constructor
        CELL_FREE(pUseCount, MEMCATEGORY_GENERAL);
		CELL_DELETE_AUTO_SHARED_MUTEX
    }

	virtual void swap(SharedPtr<T> &other) 
	{
		std::swap(pRep, other.pRep);
		std::swap(pUseCount, other.pUseCount);
		std::swap(useFreeMethod, other.useFreeMethod);
#if CELL_THREAD_SUPPORT
		std::swap(CELL_AUTO_MUTEX_NAME, other.CELL_AUTO_MUTEX_NAME);
#endif
	}
};

template<class T, class U> inline bool operator==(SharedPtr<T> const& a, SharedPtr<U> const& b)
{
	return a.get() == b.get();
}

template<class T, class U> inline bool operator!=(SharedPtr<T> const& a, SharedPtr<U> const& b)
{
	return a.get() != b.get();
}

template<class T, class U> inline bool operator<(SharedPtr<T> const& a, SharedPtr<U> const& b)
{
	return std::less<const void*>()(a.get(), b.get());
}

/** @} */
/** @} */
} // end namespace cell

#endif // CellSharedPtr_h
